package eu.goldenak.ircbot.module;

import java.io.*;
import java.lang.reflect.Array;
import java.net.*;
import java.util.*;
import java.util.regex.*;

// TODO Document!
public class LogHandler {
	public final int MODE_FTP = 1;
	public final int MODE_FILE = 2;
	
	public final int FTP_ASCII = 1;
	public final int FTP_BINARY = 2;
	
	private String serverHost;
	private int serverPort;
	private Socket rSocket;
	private BufferedReader rIn;
	private PrintWriter rOut;
	
	private String dataHost;
	private int dataPort;
	private Socket dataSocket;
	
	private String path = "";
	private String filename;
	private String username;
	private String password;
	private int mode;
	private int offset;
	private int ftpMode = FTP_ASCII;
	
	private long lastSize = 0;
	private String[] lastLines = null;
	
	private RandomAccessFile logfile = null;
		
	// Constructors
	/**
	 * Use the games_mp.log in the current directory.
	 */
	public LogHandler() {
		this("games_mp.log");
	}
	
	/**
	 * Use a different filename for the logfile.
	 */
	public LogHandler(String f) {
		this("", f);
	}
	
	/**
	 * Use a different path and filename for the logfile.
	 */
	public LogHandler(String p, String f) {
		setMode(MODE_FILE);
		setPath(p);
		setFilename(f);
		try {
			logfile = new RandomAccessFile(path + filename, "r");
		} catch (FileNotFoundException ex) {
			System.out.println("Logfile could not be found: " + path + filename);
			System.exit(1);
		}
	}
	
	/**
	 * Use FTP to retrieve the logfile.
	 */
	public LogHandler(String host, int port, String p) {
		this(host, port, p, "games_mp.log");
	}
	
	/**
	 * Use FTP and a different filename.
	 */
	public LogHandler(String host, int port, String p, String f) {
		setMode(MODE_FTP);
		setHostInfo(host, port);
		setPath(p);
		setFilename(f);
	}
	
	public void setFtpMode(int m) {
		ftpMode = m;
	}
	
	// Methods
	public String[] readNewLines() {
		if (mode == MODE_FTP) {
			if (lastSize == 0) {
				// Avoid big time spam
				lastSize = size();
				return null;
			}
			
			long newSize = size();
			// Check whether there are new messages.
			if (newSize == lastSize) return null;
			
			// Yep new messages, download them nao.
			String file;
			try {
				file = downloadFile(lastSize);
			} catch (Exception e) {
				return null;
			}
			
			// Update lastSize
			lastSize = newSize;
			String[] lines = file.split("\n");
			String[] newLines = new String[lines.length];
			int x = 0;
			
			// FIXME Still sometimes duplicate messages.
			// Seems only with slow connections.
			// Fix for duplicate messages.
			if (lastLines != null) {
				for (int i = 0; i < lines.length; i++) {
					if (Arrays.binarySearch(lastLines, lines[i]) < 0) {
						// Found new message, add.
						newLines[x++] = lines[i];
					}
				}
			} else {
				newLines = lines;
			}
			
			if (x > 0) lastLines = newLines;

			return newLines;
		} else if (mode == MODE_FILE) {
			if (lastSize == 0) {
				// Avoid big time spam
				lastSize = size();
				return null;
			}
			
			long newSize = size();
			// Check whether there are new messages.
			if (newSize == lastSize) return null;
			
			try {
				logfile.seek(lastSize);
				//System.out.println("Difference in filesize: " + (newSize - lastSize) + " bytes");
				
				lastSize = newSize;
			
				String line;
				String file = "";
				while ((line = logfile.readLine()) != null) file += line + "\n";
				file = file.substring(0, file.length() - 1);
				
				return file.split("\n");
			} catch (Exception ex) {
				return null;
			}
		} else {
			return null;
		}
	}
	
	public long size() {
		if (mode == MODE_FTP) {
			if (ftpMode == FTP_ASCII) {
				sendCmd("TYPE I");
			}
			String out = sendCmd("SIZE " + path + filename);
			String size = out.substring(out.indexOf(" ") + 1);
			
			return Long.parseLong(size);
		} else if (mode == MODE_FILE) {
			try {
				return logfile.length();
			} catch (Exception ex) {
				return lastSize;
			}
		} else {
			return 0;
		}
	}
	
	public String sendCmd(String cmd) {
		if (mode == MODE_FTP) {
			write(cmd);
			String out = read();
		
			if (out == null || out.length() < 3) {
				out = "\r\n";
			}
		
			return out;
		} else {
			return null;
		}
	}
	
	public void connect() {
		if (mode == MODE_FTP) {
			try {
				rSocket = new Socket(serverHost, serverPort);
				rIn     = new BufferedReader(new InputStreamReader(rSocket.getInputStream()));
				rOut    = new PrintWriter(rSocket.getOutputStream(), true);
			} catch (Exception ex) {
				System.out.println("Could not connect to FTP server: " + ex.getMessage());
			}
			read();
			login();
		}
	}
	
	private void login() {
		sendCmd("USER " + username);
		sendCmd("PASS " + password);
		if (ftpMode == FTP_BINARY) {
			sendCmd("TYPE I");
		}
	}
	
	private void passive() {
		String out = sendCmd("PASV");
		Pattern regex = Pattern.compile("(\\d{1,3},){5}\\d{1,3}");
		Matcher match = regex.matcher(out);
		if (match.find()) {
			String[] parts = match.group().split(",");
			
			dataPort  = Integer.parseInt(parts[5]);
			dataPort += Integer.parseInt(parts[4]) << 8;
			
			dataHost  = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
		} else {
			// Malformed message? Retry.
			passive();
		}
	}
	
	private void openDataConn() {
		if (dataSocket != null) return;
		try {
			dataSocket = new Socket(dataHost, dataPort);
		} catch (Exception e) {
			System.out.println("Could not open data connection: " + e.getMessage());
			passive();
		}
	}
	
	private void closeDataConn() throws Exception {
		if (dataSocket != null) {
			dataSocket.close();
			dataSocket = null;
		}
	}
	
	private String downloadFile(long seek) throws Exception {
		passive();
		if (ftpMode == FTP_ASCII) {
			sendCmd("TYPE A");
		}
		
		if (seek > 0) {
			sendCmd("REST " + seek);
		}
		openDataConn();
		sendCmd("RETR " + path + filename);

		BufferedReader in = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
		String out = "";
		String line;
		
		while ((line = in.readLine()) != null) {
			out += line + "\n";
		}
		closeDataConn();
		read();
		
		return out;
	}
	
	private String read() {
		try {
			String in = rIn.readLine();
			//System.out.println("FTP READ: " + in);
			return in;
		} catch (IOException ex) {
			// Reconnect
			connect();
		}
		
		return null;
	}
	
	private void write(String line) {
		rOut.println(line);
		//System.out.println("FTP WRITE: " + line);
	}
	
	// Get and setters.
	public String getFilename() {
		return filename;
	}
	
	public void setFilename(String f) {
		filename = f;
	}
	
	public void setHostInfo(String host, int port) {
		serverHost = host;
		serverPort = port;
	}
	
	public String getHost() {
		return serverHost;
	}
	
	public int getPort() {
		return serverPort;
	}
	
	public int getMode() {
		return mode;
	}
	
	public void setMode(int m) {
		mode = m;
	}
	
	public void setPassword(String p) {
		password = p;
	}
	
	public String getPath() {
		return path;
	}
	
	public void setPath(String p) {
		p = p.replace('\\', '/');
		if (!p.substring(p.length() - 1).equals("/")) {
			// Add a slash to the end.
			p += "/";
		}
		
		path = p;
	}
	
	public void setUsername(String u) {
		username = u;
	}
}